const express = require("express");
const config = require("./lib/config/config.js");
const path = require("path");
const fs = require("fs");
const core = require("./lib/core.js");

/**
 * @description 基于express封装的mvc框架，遵循约定优于配置原则
 */
class Chan {
  constructor(options={}) {
    //配置
    Chan.config = {...config,...options};
    //模块
    Chan.modules ={};
    //插件
    Chan.plugins ={};
    //工具
    Chan.helper ={};
    //应用实例
    this.app = express();
    this.app.config = Chan.config;
    this.router = express.Router();
    //加载配置
    this.loadConfig();
    //加载扩展
    this.loadExtends();
    //加载核心（日志、favicon 图标、cookie、json、url、模板引擎、静态资源）
    this.loadCore();
    //加载实例化数据库
    this.loadKnex();
    //生命周期钩子：开始启动 
    this.beforeStart();
    //加载模块
    this.loadModules();
    //加载插件
    this.loadPlugins();
    //加载通用路由
    this.loadCommonRouter();
    //生命周期：初始化完成
    this.start()
  }

  // 加载配置
  loadConfig() {
    const configPath = path.join(config.APP_PATH, "config/index.js");
    if (fs.existsSync(configPath)) {
      const config = require(configPath);
      let allConfig = {...Chan.config,...config};
      this.app.config = Chan.config = allConfig;
    }
  }

   // 加载扩展
   loadExtends() {
    const extendPath = path.join(config.APP_PATH, "extend");
    if (fs.existsSync(extendPath)) {
      let controllers = fs
        .readdirSync(extendPath)
        .filter((file) => file.endsWith(".js"));
      for (let i = 0, file; i < controllers.length; i++) {
        file = controllers[i];
        const helper = require(path.join(extendPath, file));
        const fileName = file.replace(".js", "");
        Chan.helper[fileName] = helper;
      }
    }
  
  }

  //数据库操作
  loadKnex(){
    // 连接数据库
    const {host,port,user,password,database,client,charset} = Chan.config.database;
    const knex = require("knex")({
      client: "mysql2",
      connection: {
        host,
        port,
        user,
        password,
        database,
        charset,
      },
      debug: config.debug, //指明是否开启debug模式，默认为true表示开启
      pool: {
        //指明数据库连接池的大小，默认为{min: 2, max: 10}
        min: 0,
        max: 2,
      },
      log: {
        warn(message) {
          console.error("[knex warn]", message);
        },
        error(message) {
          console.error("[knex error]", message);
        },
      },
    });
    Chan.knex = knex;
  }

  //开始启动
  beforeStart(cb) {
    // 初始化一些配置
    cb && cb();
  }

  //启动
  start(cb){
    // 初始化一些配置
    cb && cb();
  }

  // 加载插件
  loadPlugins() {
    this.loadModules('plugins');
  }

  /**
   * @description app核心模块：日志、favicon 图标、cookie、json、url、模板引擎、静态资源
   */
  loadCore(){
    core(this.app);
  }

 /**
  * @description 模块加载入口（路由&控制器& 服务）
  */
  loadModules(modules='modules') {
    const configPath = path.join(config.APP_PATH,modules);
    if (fs.existsSync(configPath)) {
      const dirs = fs
        .readdirSync(configPath, { withFileTypes: true })
        .filter((dirent) => dirent.isDirectory())
        .map((dirent) => dirent.name);
        Chan[modules]={};
        for(let i=0,item;i<dirs.length;i++){
          item = dirs[i];
          Chan[modules][item] = {
            controller: {},
            service: {},
          };
          this.loadModule(modules,item);
        }
      //执行路由
      // this.app.use(this.router);
    }
  }


  /**
   * @description 加载模块，包括 controller service router 
   * @param {String} moduleName 模块名称
   */
  loadModule(modules,moduleName) {
    this.loadServices(modules,moduleName);
    this.loadControllers(modules,moduleName);
    this.loadRoutes(modules,moduleName);
  }


  /**
   * @description 扫描模块下所有service
   * @param {*} moduleDir 模块路径
   * @param {*} moduleName 模块名称
   */
  loadServices(modules,moduleName) {
    const servicesDir = path.join(config.APP_PATH, modules, moduleName,"service");
    if (fs.existsSync(servicesDir)) {
      let services = fs
        .readdirSync(servicesDir)
        .filter((file) => file.endsWith(".js"));
      for (let i = 0, file; i < services.length; i++) {
        file= services[i]
        const Service = require(path.join(servicesDir, file));
        const serviceName = file.replace(".js", "");
        //模块文件夹-模块文件名-服务-方法
        Chan[modules][moduleName].service[serviceName] = {};
        Chan[modules][moduleName].service[serviceName] = Service;
      }
    }
  }


  /**
   * @description 扫描模块下所有controller
   * @param {*} moduleDir 模块路径
   * @param {*} moduleName 模块名称
   */
  loadControllers(modules,moduleName) {
    const controllersDir = path.join(config.APP_PATH, modules, moduleName,"controller");
    if (fs.existsSync(controllersDir)) {
      let controllers = fs
        .readdirSync(controllersDir)
        .filter((file) => file.endsWith(".js"));
      for (let i = 0, file; i < controllers.length; i++) {
        file= controllers[i]
        const controller = require(path.join(controllersDir, file));
        const controllerName = file.replace(".js", "");
        Chan[modules][moduleName].controller[controllerName] = {};
        Chan[modules][moduleName].controller[controllerName] = controller;
      }
    }
  }

  /**
   * @description 扫描模块下所有router.js
   * @param {*} moduleDir 模块路径
   * @param {*} moduleName 模块名称
   */
  loadRoutes(modules,moduleName) {
    const routersDir = path.join(config.APP_PATH, modules, moduleName,"router.js");
    if (fs.existsSync(routersDir)) {
      const routes = require(routersDir);
      routes({router:this.router,modules:Chan[modules],app:this.app});
    }
  }

   //通用路由，加载错误处理和500路由和爬虫处理
  loadCommonRouter(){
    try {
      const baseRouterPath = path.join(config.APP_PATH, "router.js");
      if (fs.existsSync(baseRouterPath)) {
        const _router = require(baseRouterPath);
        _router(this.app, this.router);
      } 
    } catch (error) {
      console.log(error);
    }
  }
  
  run() {
    const port = this.app.config.port || '81';
    this.app.listen(port, () => {
      console.log(`Server is running on port ${port}`);
    });
  }
}

module.exports = Chan;